/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦，本文采用木兰宽松许可证第2版]
 * 
 * https://zhiqim.org/project/zhiqim_kit/zhiqim_git.htm
 *
 * ZhiqimGit is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.git;

import static java.util.zip.Deflater.DEFAULT_COMPRESSION;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.zhiqim.kernel.config.Config;
import org.zhiqim.kernel.config.Group;
import org.zhiqim.kernel.util.Files;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.kernel.util.Systems;
import org.zhiqim.kernel.util.Validates;

/**
 * Git配置文件/${gitRootDir}/config
 *
 * @version v1.0.0 @author zhichenggang 2017-3-26 新建与整理
 */
public class GitConfig implements GitConstants
{
    private final Config gitconfig;            //${user.home}/.gitconfig
    private final Config config;               //${gitdir}/config
    
    private final String authorName;
    private final String authorEmail;
    private final String committerName;
    private final String committerEmail;

    public GitConfig(File configFile)
    {
        //判断${user.home}/.gitconfig
        File file = new File(Systems.getUserHome(), ".gitconfig");
        if (Files.isFile(file))
            this.gitconfig = new Config("gitconfig", file.getAbsolutePath(), false, true);
        else
            this.gitconfig = null;
        
        //${gitdir}/config
        this.config = new Config("config", configFile.getAbsolutePath(), false, true);

        authorName = chkUserName(GIT_AUTHOR_NAME_KEY);
        authorEmail = chkUserEmail(GIT_AUTHOR_EMAIL_KEY);

        committerName = chkUserName(GIT_COMMITTER_NAME_KEY);
        committerEmail = chkUserEmail(GIT_COMMITTER_EMAIL_KEY);
    }

    public String groupId(String section, String subsection)
    {
        return (Validates.isEmpty(subsection)) ? section : (section + " \"" + subsection + "\"");
    }
    
    public void updateBranch(String oldSection, String newSection)
    {
        String oldGroupId = oldSection + " \"";
        List<String> oldList = new ArrayList<>();
        
        //1.找出所有分枝的组，把新的增加进去
        Collection<Group> groupList = config.getGroupList();
        for (Group group : groupList)
        {
            String groupId = group.getId();
            if (!groupId.startsWith(oldGroupId))
                continue;
            
            oldList.add(groupId);
            String newId = newSection + oldSection.substring(oldSection.length());
            Group newGroup = group.clone().setId(newId);
            config.addGroup(newGroup);
        }
        
        //2.删除原组
        for (String old : oldList)
        {
            config.removeGroup(old);
        }
    }

    /****************************************************************/
    // 加载&保存
    /****************************************************************/

    /** 判断config文件是否存在 */
    public boolean exists()
    {
        return Files.exists(config.getPath());
    }

    /** 加载配置文件 */
    public void load() throws IOException
    {
        if (exists())
            config.load();
        
        if (gitconfig != null)
            gitconfig.load();
    }

    /** 保存配置文件 */
    public void save() throws IOException
    {
        config.save();
    }

    /****************************************************************/
    // 两个重要的获取&设置方法
    /****************************************************************/
    
    /**
     * 读取配置字符串值，先取${gitdir}/config，如果为null，再读${user.home}/.gitconfig，还为null，返回缺省值
     * 
     * @param section           区域编码
     * @param subsection        子区域编码
     * @param name              配置名称
     * @param defaultValue      缺省值
     * @return                  返回读取的值
     */
    public String getString(String section, String subsection, String name, String defaultValue)
    {
        String value = config.getString(groupId(section, subsection), name);
        if (value == null && gitconfig != null)
        {//未读取值，判断.gitconfig
            value = gitconfig.getString(groupId(section, subsection), name);
        }
        
        return (value == null)?defaultValue:value;
    }
    
    /**
     * 设置值到配置缓存中
     * 
     * @param section           区域编码
     * @param subsection        子区域编码
     * @param name              配置名称
     * @param value             配置值
     */
    public void setString(String section, String subsection, String name, String value)
    {
        config.setString(groupId(section, subsection), name, value);
    }
    
    /**
     * 读取配置长整型值，支持单位（g、m、k、G、M、K）
     * 
     * @param section           区域编码
     * @param subsection        子区域编码
     * @param name              配置名称
     * @param defaultValue      缺省值
     * @return                  返回读取的值
     */
    public long getLong(final String section, String subsection, final String name, final long defaultValue)
    {
        String str = getString(section, subsection, name);
        if (str == null)
            return defaultValue;

        String num = Strings.trim(str);
        if (num.length() == 0)
            return defaultValue;

        long mul = 1;
        switch (num.charAt(num.length() - 1))
        {//取最后一个字符，看是不是g、m、k单位
        case 'g':
        case 'G':
            mul = GiB;
            break;
        case 'm':
        case 'M':
            mul = MiB;
            break;
        case 'k':
        case 'K':
            mul = KiB;
            break;
        }
        
        if (mul > 1)
            num = num.substring(0, num.length() - 1).trim();
        
        if (num.length() == 0)
            return defaultValue;

        try
        {
            return mul * Long.parseLong(num);
        }
        catch (NumberFormatException nfe)
        {
            throw new IllegalArgumentException("非法的整数值[" + section + "." + name + "]=" + str);
        }
    }
    
    /**
     * 设置长整型值到缓存中，支持转换单位（g、m、k），请设置单位的整数倍
     * 
     * @param section           区域编码
     * @param subsection        子区域编码
     * @param name              配置名称
     * @param value             配置值
     */
    public void setLong(String section, String subsection, String name, long value)
    {
        String s = null;

        if (value >= GiB && (value % GiB) == 0)
            s = String.valueOf(value / GiB) + " g";
        else if (value >= MiB && (value % MiB) == 0)
            s = String.valueOf(value / MiB) + " m";
        else if (value >= KiB && (value % KiB) == 0)
            s = String.valueOf(value / KiB) + " k";
        else
            s = String.valueOf(value);

        setString(section, subsection, name, s);
    }
    
    
    /****************************************************************/
    // 删除配置信息
    /****************************************************************/
    
    public void unset(String section, String subsection, String name)
    {
        config.remove(groupId(section, subsection), name);
    }

    public void unsetSection(String section, String subsection)
    {
        config.removeGroup(groupId(section, subsection));
    }
    
    /****************************************************************/
    // 获取方法
    /****************************************************************/

    public String getString(String section, String name)
    {
        return getString(section, null, name);
    }

    public String getString(String section, String subsection, String name)
    {
        return getString(section, subsection, name, null);
    }

    public long getLong(String section, String name, long defaultValue)
    {
        return getLong(section, null, name, defaultValue);
    }
    
    public int getInt(String section, String name, int defaultValue)
    {
        return getInt(section, null, name, defaultValue);
    }

    public int getInt(String section, String subsection, String name, int defaultValue)
    {
        final long val = getLong(section, subsection, name, defaultValue);
        if (Integer.MIN_VALUE <= val && val <= Integer.MAX_VALUE)
            return (int) val;

        throw new IllegalArgumentException("整数值[" + section + "." + name + "]超过int范围");
    }

    public boolean getBoolean(String section, String subsection, String name, boolean defaultValue)
    {
        String n = getString(section, subsection, name);
        if (Validates.isEmptyBlank(n))
            return defaultValue;

        return Strings.toBooleanWide(n);
    }

    public boolean getBoolean(String section, String name, boolean defaultValue)
    {
        return getBoolean(section, null, name, defaultValue);
    }

    /****************************************************************/
    // 设置方法
    /****************************************************************/

    public void setString(String section, String name, String value)
    {
        setString(section, null, name, value);
    }

    public void setLong(String section, String name, long value)
    {
        setLong(section, null, name, value);
    }

    public void setInt(String section, String name, int value)
    {
        setLong(section, null, name, value);
    }

    public void setInt(String section, String subsection, String name, int value)
    {
        setLong(section, subsection, name, value);
    }

    public void setBoolean(String section, String name, boolean value)
    {
        setString(section, name, String.valueOf(value));
    }

    public void setBoolean(String section, String subsection, String name, boolean value)
    {
        setString(section, subsection, name, String.valueOf(value));
    }

    /****************************************************************************************/
    // 核心获取值的方法
    /****************************************************************************************/

    /** 获取是否信任文件权限模式 */
    public boolean isFileMode()
    {
        return getBoolean(_SECTION_CORE_, _KEY_FILE_MODE_, true);
    }
    
    /** 获取隐藏点开头的文件方式，true|false|dotGitOnly三种情况，缺省dotGitOnly （Windows有效） */
    public String getHideDotFiles()
    {
        return getString(_SECTION_CORE_, _KEY_HIDE_DOT_FILES_, "dotGitOnly");
    }

    /** 仓库格式版本，默认0 */
    public int getRepositoryFormatVersion()
    {
        return getInt(_SECTION_CORE_, _KEY_REPOSITORY_FORMAT_VERSION_, 0);
    }

    /** 压缩级别 -1 .. 9之间数字，0表示不压缩，缺省-1表示zlib，1-9表示压缩速度，1表示最快压缩但压缩比最小，9压缩比最大但最慢 */
    public int getCompression()
    {
        return getInt(_SECTION_CORE_, _KEY_COMPRESSION_, DEFAULT_COMPRESSION);
    }
    
    /** 获取工作目录 */
    public String getWorkTree()
    {
        return getString(_SECTION_CORE_, _KEY_WORKTREE_);
    }

    /** 是否所有引用更新记录日志 true|false，默认取true */
    public boolean isLogAllRefUpdates()
    {
        return getBoolean(_SECTION_CORE_, _KEY_LOG_ALL_REF_UPDATES_, true);
    }

    /** 排除文件 */
    public String getExcludesFile()
    {
        return getString(_SECTION_CORE_, _KEY_EXCLUDESFILE_);
    }

    /** 是否同步对象文件 */
    public boolean isFsyncObjectFiles()
    {
        return getBoolean(_SECTION_CORE_, _KEY_FSYNC_OBJECT_FILES_, false);
    }

    /** 是否同步引用文件 */
    public boolean isFsyncRefFiles()
    {
        return getBoolean(_SECTION_CORE_, "fsyncRefFiles", false);
    }
    
    /** 设置是否信任文件权限模式 */
    public GitConfig setFileMode(boolean isFileMode)
    {
        setBoolean(_SECTION_CORE_, _KEY_FILE_MODE_, isFileMode);
        return this;
    }
    
    /** 设置仓库格式版本，默认0 */
    public GitConfig setRepositoryFormatVersion(int repositoryFormatVersion)
    {
        setInt(_SECTION_CORE_, _KEY_REPOSITORY_FORMAT_VERSION_, repositoryFormatVersion);
        return this;
    }
    
    /** 设置是否所有引用更新记录日志 true|false，默认取true */
    public GitConfig setLogAllRefUpdates(boolean isLogAllRefUpdates)
    {
        setBoolean(_SECTION_CORE_, _KEY_LOG_ALL_REF_UPDATES_, isLogAllRefUpdates);
        return this;
    }
    
    /** 设置文本属性是否自动换行 true|false|input三种 */
    public GitConfig setAutoCRLF(boolean isAutoCRLF)
    {
        setBoolean(_SECTION_CORE_, _KEY_AUTOCRLF_, isAutoCRLF);
        return this;
    }
    
    /** 设置是否裸仓库 true| false */
    public GitConfig setBare(boolean isBare)
    {
        setBoolean(_SECTION_CORE_, _KEY_BARE_, isBare);
        return this;
    }
    
    /****************************************************************************************/
    // pack&repack获取值的方法
    /****************************************************************************************/

    /** 索引版本 */
    public int getPackIndexVersion()
    {
        return getInt(_SECTION_PACK_, _KEY_INDEX_VERSION_, _DEFAULT_INDEX_VERSION_);
    }

    /** 包压缩级别，如果没有取核心配置压缩级别 */
    public int getCompressionLevel()
    {
        return getInt(_SECTION_PACK_, _KEY_COMPRESSION_, getCompression());
    }
    
    /** 包支持的深度 */
    public int getDepth()
    {
        return getInt(_SECTION_PACK_, _KEY_DEPTH_, _DEFAULT_DEPTH_);
    }
    
    /** 是否支持增量偏移，默认true */
    public boolean getUseDeltaBaseOffset()
    {
        return getBoolean(_SECTION_REPACK_, _KEY_USE_DELTA_BASE_OFFSET_, true);
    }

    /****************************************************************************************/
    // 接收获取值的方法
    /****************************************************************************************/

    public boolean isReceiveFsckObjects()
    {
        return getBoolean(_SECTION_RECEIVE_, _KEY_FSCK_OBJECTS_, false);
    }

    public boolean isReceiveDenyDeletes()
    {
        return getBoolean(_SECTION_RECEIVE_, _KEY_DENY_DELETES_, false);
    }

    public boolean isReceiveDenyNonFastForwards()
    {
        return getBoolean(_SECTION_RECEIVE_, _KEY_DENY_NON_FAST_FORWARDS_, false);
    }

    /****************************************************************************************/
    // 上传包获取值的方法
    /****************************************************************************************/

    /** 允许隐藏引用被抓取 */
    public boolean isAllowTipSha1InWant()
    {
        return getBoolean(_SECTION_UPLOADPACK_, _KEY_ALLOW_TIP_SHA1_IN_WANT_, false);
    }

    /** 从所有地方抓取对象 */
    public boolean isAllowReachableSha1InWant()
    {
        return getBoolean(_SECTION_UPLOADPACK_, _KEY_ALLOW_REACHABLE_SHA1_IN_WANT_, false);
    }

    /****************************************************************************************/
    // 用户获取值的方法
    /****************************************************************************************/

    public String getAuthorName()
    {
        return authorName;
    }

    public String getAuthorEmail()
    {
        return authorEmail;
    }

    public String getCommitterName()
    {
        return committerName;
    }

    public String getCommitterEmail()
    {
        return committerEmail;
    }

    /****************************************************************************************/
    // 私有的初始化用户的名称和邮箱
    /****************************************************************************************/
    
    private String chkUserName(String envKey)
    {
        String username = getString("user", "name");
        if (username == null)
        {// 尝试环境变更
            username = Systems.getEnv(envKey);
        }

        if (username == null)
        {// 尝试取系统用户名
            username = Systems.getUserName();
        }

        return username;
    }

    private String chkUserEmail(String envKey)
    {
        String email = getString("user", "email");
        if (email == null)
        {// 尝试环境变更
            email = Systems.getEnv(envKey);
        }

        if (email == null)
        {// 尝试系统用户名
            String username = Systems.getUserName();
            email = username + "@" + Systems.getHosthame();
        }

        return email;
    }
}
